﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using QueryParameter = System.Collections.Generic.KeyValuePair<string, string>;

namespace RestFullChatAJAX
{
    /*
     * Process QueryParameter (String process, endcode...) from Request
     * */
    public static class UriUtility
    {
        const string AuthorizationHeaderRealmParameter = "realm";
        const string OAuthAuthorizationHeaderStart = "OAuth";
        const string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
        static readonly string[] QuoteCharacters = new[] { "\"", "'" };
        /// <summary>
        /// Extracts all the parameters from the supplied encoded parameters. 
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static List<QueryParameter> GetHeaderParameters(string parameters)
        {
            parameters = parameters.Trim();

            var result = new List<QueryParameter>();

            if (!parameters.StartsWith(OAuthAuthorizationHeaderStart, StringComparison.InvariantCultureIgnoreCase))
            {
                return result;
            }

            parameters = parameters.Substring(OAuthAuthorizationHeaderStart.Length).Trim();

            if (!string.IsNullOrEmpty(parameters))
            {
                string[] p = parameters.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (string s in p)
                {
                    if (string.IsNullOrEmpty(s)) continue;
                    QueryParameter parameter = ParseAuthorizationHeaderKeyValuePair(s);
                    if (parameter.Key != AuthorizationHeaderRealmParameter)
                    {
                        result.Add(parameter);
                    }
                }
            }

            return result;
        }

        static QueryParameter ParseAuthorizationHeaderKeyValuePair(string value)
        {
            if (value.IndexOf('=') > -1)
            {
                string[] temp = value.Split('=');
                return new QueryParameter(temp[0].Trim(), StripQuotes(temp[1]));
            }
            return new QueryParameter(value.Trim(), string.Empty);
        }

        static string StripQuotes(string quotedValue)
        {
            foreach (string quoteCharacter in QuoteCharacters)
            {
                if (quotedValue.StartsWith(quoteCharacter) && quotedValue.EndsWith(quoteCharacter) && quotedValue.Length > 1)
                {
                    return quotedValue.Substring(1, quotedValue.Length - 2);
                }
            }

            return quotedValue;
        }

        /// <summary>
        /// An OAuth specific implementation of Url Encoding - we need this to produce consistent base
        /// strings to other platforms such as php or ruby, as the <see cref="HttpUtility.UrlEncode" />
        /// doesn't encode in the same way.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string UrlEncode(string value)
        {
            if (value == null) return null;

            var result = new StringBuilder();

            foreach (char symbol in value)
            {
                if (UnreservedChars.IndexOf(symbol) != -1)
                {
                    result.Append(symbol);
                }
                else
                {
                    result.Append('%' + String.Format("{0:X2}", (int)symbol));
                }
            }

            return result.ToString();
        }

        /// <summary>
        /// Formats a set of query parameters, as per query string encoding.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static string FormatQueryString(IEnumerable<KeyValuePair<string, string>> parameters)
        {
            var builder = new StringBuilder();

            if (parameters != null)
            {
                foreach (var pair in parameters)
                {
                    if (builder.Length > 0) builder.Append("&");
                    builder.Append(pair.Key).Append("=");
                    builder.Append(UrlEncode(pair.Value));
                }
            }

            return builder.ToString();
        }

        /// <summary>
        /// Formats a set of query parameters, as per query string encoding.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static string FormatQueryString(NameValueCollection parameters)
        {
            var builder = new StringBuilder();

            if (parameters != null)
            {
                foreach (string key in parameters.Keys)
                {
                    if (builder.Length > 0) builder.Append("&");
                    builder.Append(key).Append("=");
                    builder.Append(UrlEncode(parameters[key]));
                }
            }

            return builder.ToString();
        }

        /// <summary>
        /// Takes an http method, url and a set of parameters and formats them as a signature base as per the OAuth core spec.
        /// </summary>
        /// <param name="httpMethod"></param>
        /// <param name="url"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static string FormatParameters(string httpMethod, Uri url, List<QueryParameter> parameters)
        {
            string normalizedRequestParameters = NormalizeRequestParameters(parameters);

            var signatureBase = new StringBuilder();
            signatureBase.AppendFormat("{0}&", httpMethod.ToUpper());

            signatureBase.AppendFormat("{0}&", UrlEncode(NormalizeUri(url)));
            signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters));

            return signatureBase.ToString();
        }

        /// <summary>
        /// Normalizes a sequence of key/value pair parameters as per the OAuth core specification.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static string NormalizeRequestParameters(IEnumerable<QueryParameter> parameters)
        {
            IEnumerable<QueryParameter> orderedParameters = parameters
              .OrderBy(x => x.Key)
              .ThenBy(x => x.Value)
              .Select(
              x => new QueryParameter(x.Key, UrlEncode(x.Value)));

            var builder = new StringBuilder();

            foreach (var parameter in orderedParameters)
            {
                if (builder.Length > 0) builder.Append("&");

                builder.Append(parameter.Key).Append("=").Append(parameter.Value);
            }

            return builder.ToString();
        }

        /// <summary>
        /// Normalizes a Url according to the OAuth specification (this ensures http or https on a default port is not displayed
        /// with the :XXX following the host in the url).
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static string NormalizeUri(Uri uri)
        {
            string normalizedUrl = string.Format("{0}://{1}", uri.Scheme, uri.Host);

            if (!((uri.Scheme == "http" && uri.Port == 80) ||
                  (uri.Scheme == "https" && uri.Port == 443)))
            {
                normalizedUrl += ":" + uri.Port;
            }

            return normalizedUrl + ((uri.AbsolutePath == "/") ? "" : uri.AbsolutePath);
        }

        public static IEnumerable<QueryParameter> ToQueryParameters(this NameValueCollection source)
        {
            foreach (string key in source.AllKeys)
            {
                yield return new QueryParameter(key, source[key]);
            }
        }     
    }
}
